package com.idanchuang.count.dao.impl;

import com.idanchuang.component.core.util.StringUtil;
import com.idanchuang.count.core.entity.IncrementCommand;
import com.idanchuang.count.core.entity.ResetCommand;
import com.idanchuang.count.dao.CountDao;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Richard_yyf
 * @version 1.0 2021/5/28
 */
@Component
public class CountDaoImpl implements CountDao {

    @Resource(name = "stringRedisTemplate")
    private StringRedisTemplate redisTemplate;

    @Override
    public Long getCount(String key) {
        String ret = redisTemplate.opsForValue().get(key);
        if (StringUtil.isBlank(ret)) {
            return null;
        }
        return Long.valueOf(ret);
    }

    @Override
    public List<Long> multiGetCount(List<String> keys) {

        // list of values associated with the given fields, in the same order as they are requested.
        List<String> retList = redisTemplate.opsForValue().multiGet(keys);

        if (retList == null) {
            return Collections.emptyList();
        }

        return retList.stream()
                .map(r -> StringUtil.isEmpty(r) ? null : Long.valueOf(r))
                .collect(Collectors.toList());
    }

    @Override
    public Long incr(IncrementCommand incrementCommand) {
        return redisTemplate.opsForValue().increment(incrementCommand.getKey(), incrementCommand.getIncrement());
    }

    @Override
    public List<Long> batchIncr(List<? extends IncrementCommand> commands) {

        List<Object> results =  redisTemplate.executePipelined((RedisCallback<String>) redisConnection -> {

            // 依次放入命令
            commands.forEach(command
                    -> redisConnection.incrBy(keyToByteArray(command.getKey()), command.getIncrement()));

            return null;
        });

        return results.stream()
                .map(count -> (Long) count)
                .collect(Collectors.toList());
    }

    @Override
    public void batchSet(List<ResetCommand> resetCommands) {

        redisTemplate.executePipelined((RedisCallback<String>) redisConnection -> {

            // 依次放入命令
            resetCommands.forEach(command
                    -> redisConnection.set(keyToByteArray(command.getKey()), valToByteArray(command.getVal())));
            return null;
        });

    }

    @Override
    public Long executeScript(RedisScript<Long> script, List<String> keys, String... args) {
        return redisTemplate.execute(script, keys, args);
    }

    private byte[] keyToByteArray(String key) {
        return key.getBytes(StandardCharsets.UTF_8);
    }

    private byte[] valToByteArray(Long count) {
        return String.valueOf(count).getBytes(StandardCharsets.UTF_8);
    }
}
